/*
 * Course:        COMP 380
 * Semester:      Summer 2012
 * Assignment:    Text To Speech
 * File Name:     Text_to_speechActivity.java
 * Author:        James Paul Pan
 * 				  Kenneth Wakeland
 * 				  Peter Xaymountry
 * Group #:		  Group #3
 */

package com.comp380.texttospeech;

import android.app.Activity;
import android.app.AlertDialog;
import android.app.ProgressDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.media.AudioManager;
import android.media.MediaPlayer;
import android.media.MediaPlayer.OnCompletionListener;
import android.os.AsyncTask;
import android.os.Bundle;
import android.os.Environment;
import android.speech.tts.TextToSpeech;
import android.view.MotionEvent;
import android.view.View.OnClickListener;
import android.view.View.OnTouchListener;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.Toast;
import android.view.View;
import android.view.inputmethod.InputMethodManager;
import android.widget.EditText;
import android.speech.tts.TextToSpeech.OnInitListener;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.OptionalDataException;
import java.io.StreamCorruptedException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Locale;
import java.util.Random;

public class Text_to_speechActivity extends Activity implements OnClickListener, OnTouchListener, OnInitListener 
{
    /** Called when the activity is first created. */
	
	// Create an Array of the file which is default to null.
	public ArrayList<FileInformation> audioFileArrayList = null;
	
	
	private int MY_DATA_CHECK_CODE = 0;
	
	private TextToSpeech myTTS;
	
	// Initialize the button variables.
	Button btnSpeak, btnSaved, btnNavMain, btnHelp, 
		   btnNavSaved, btnNavShare, btnNavImport, mSaveAudioButton;
	
	// Initialize the editext variable.
	EditText txtInput;
	
	Context mContext;
	
	// Initialize and set the keyboard button check variable to false.
	boolean mIsKeyPadButtonChecked = false;
	
	// Initialize the image view variables.
	ImageView mKeyPadButton, btnClear;
	
	// Initialize the string variables.
	String destFileName, audioFilePath;
	
	// Initialize and set the mediaplayer variable to null.
	MediaPlayer mMediaPlayer = null;
	
	// Initialize and set the sound file to bull.
	File soundFile = null;

	// Initialize the directory variable.
	String test_to_speech_directory = "";

    @Override
    public void onCreate(Bundle savedInstanceState) 
    {
    	// Keep the screen orientation in portrait mode
    	super.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
    	
        super.onCreate(savedInstanceState);
        
        // Set the layout for this screen.
        setContentView(R.layout.main2);
        
        // Set the Text to Speech directory to the SD card and create the folder.
    	test_to_speech_directory = Environment.getExternalStorageDirectory().getAbsolutePath()  + File.separatorChar + getString(R.string.name_directory);

    	// Create the Text To Speech folder in your SD card.
        createDirectory(test_to_speech_directory);

        // Set the context to this.
        mContext = this;
        
        // Pass the variable to correspond with the Speak button.
        btnSpeak = (Button) findViewById(R.id.btnSpeak2);
        
        // When the button is click it goes to the onClick method and speak
        // the text that the user type in.
        btnSpeak.setOnClickListener(this);
        
        // Pass the variable to correspond with the Save button.
        btnSaved = (Button) findViewById(R.id.headphones_down);
        
        // When the button is click it goes to the onClick method and it
        // saved the user type audio file.
        btnSaved.setOnClickListener(this);
        
        // Pass the variable to correspond with the Main button.
        btnNavMain = (Button) findViewById(R.id.btnNavMain2);
        
        // When the button is click it goes to the onClick method and take
        // the user to the Main screen.
        btnNavMain.setOnClickListener(this);
        
        // Pass the variable to correspond with the Saved button.
        btnNavSaved = (Button) findViewById(R.id.btnNavSaved2);
        
        // When the button is click it goes to the onClick method and take
        // the user to the Saved screen.
        btnNavSaved.setOnClickListener(this);
        
        // Pass the variable to correspond with the Share button.
        btnNavShare = (Button) findViewById(R.id.btnNavShare2);
        
        // When the button is click it goes to the onClick method and take
        // the user to the Share screen.
        btnNavShare.setOnClickListener(this);
        
        // Pass the variable to correspond with the Import button.
        btnNavImport = (Button) findViewById(R.id.btnNavImport2);
        
        // When the button is click it goes to the onClick method and take
        // the user to the Import screen.
        btnNavImport.setOnClickListener(this);
        
        // Pass the variable to correspond with the Folder button.
        mSaveAudioButton = (Button) findViewById(R.id.btnSaved2);
        
        // When the button is click it goes to the onClick method and take
        // the user to the SavedAudio screen.
        mSaveAudioButton.setOnClickListener(this);
        
        // Pass the variable to correspond with the Help button.
        btnHelp = (Button) findViewById(R.id.buttonHelp);
        
        // When the button is click it goes to the onClick method and take
        // the user to the Help screen.
        btnHelp.setOnClickListener(this);
        
        // Pass the variable to correspond with the Keyboard button.
        mKeyPadButton = (ImageView) findViewById(R.id.keyboard);
        
        // When the button is click it goes to the onClick method and it
        // hide the keyboard if its activated.
        mKeyPadButton.setOnClickListener(this);
        
        // Pass the variable to correspond with the Eraser button.
        btnClear = (ImageView) findViewById(R.id.btnClear);
        
        // When the button is click it goes to the onClick method and it
        // clear the text in the text field.
        btnClear.setOnClickListener(this);
        
        // Pass the variable to correspond with the Text edit text field.
        txtInput = (EditText) findViewById(R.id.txtInput2);
        
        // When the button is click it goes to the onClick method and let
        // the user type in the text field.
        txtInput.setOnTouchListener(this);
        
        // Initialize a new intent variable.
        Intent checkTTSIntent = new Intent();
        
        // This set the action to perform, so in this case it check the data.
        checkTTSIntent.setAction(TextToSpeech.Engine.ACTION_CHECK_TTS_DATA);
        
        // Start the activity.
        startActivityForResult(checkTTSIntent, MY_DATA_CHECK_CODE);              
    }
    
    /*
     * This method will check if the user touch the text input, if they do
     * it will set the boolean mIsKeyPadButtonChecked to true and that will
     * represent that the keyboard is showing
     */
	public boolean onTouch(View v, MotionEvent event) 
	{
		// Set the variable to be true.
		mIsKeyPadButtonChecked = true;
		return false;
	}
    
	
	/*
	 * This method will handle what happen when the user click on a particular 
	 * button.
	 */
    public void onClick(View v)
    {
    	switch(v.getId())
    	{
            case R.id.btnSpeak2 : 
            						// This will speak what the user wrote in the text field.
                					speakUtterance();
                					break;

            case R.id.btnSaved2 : 	
            						// This will save the text audio file.
                					saveAudioToFile();
                					break;

            case R.id.btnNavSaved2 : 
                					// Display a message that the user click on the Saved button.
                					Toast.makeText(getBaseContext(), "Going to the Saved screen", Toast.LENGTH_SHORT).show();

                					// This will start the transition to the SavedAudio screen.
                					startActivity(new Intent(this, SavedAudio.class));
                					break;

            case R.id.btnNavShare2 : 
                					// Display a message that the user click on the Share button.
                					Toast.makeText(getBaseContext(), "Going to the Share screen", Toast.LENGTH_SHORT).show();
                					
                					// This will start the transition to the Share screen.
                					startActivity(new Intent(this, share.class));
                					break;

            case R.id.btnNavImport2 : 
                					// Display a message that the user click on the Import button.
                					Toast.makeText(getBaseContext(), "Going to the Import screen", Toast.LENGTH_SHORT).show();
                					
                					// This will start the transition to the Import screen.
                					startActivity(new Intent(this, importwindow.class));
                					break;

            case R.id.btnNavMain2 : 	
                					// Display a message that the user click on the Main button.
                					Toast.makeText(getBaseContext(), "Your already in the Main screen", Toast.LENGTH_SHORT).show();
                					break;

            case R.id.buttonHelp:		
                					// Display a message that the user click on the Help button.
                					Toast.makeText(getBaseContext(), "Going to Help screen", Toast.LENGTH_SHORT).show();
                					
                					// This will start the transition to the Help screen.
                					startActivity(new Intent(this, Help.class));
                					break;

            case R.id.btnClear : 
                					// Display a message that the user click on the Clear button.
                					Toast.makeText(getBaseContext(), "Text is clear", Toast.LENGTH_SHORT).show();
                					
                					// This set the text to blank again.
                					txtInput.setText("");
                					break;

            case R.id.keyboard : 
                					// Display a message that the user click on the Keyboard button.
                					Toast.makeText(getBaseContext(), "You click the keyboard button", Toast.LENGTH_SHORT).show();
                					
                					// This will hide the phone keyboard interface
                					toggleKeyboard();
                					break;

            case R.id.headphones_down : 
                					// Display a message that the user click on the Saved button.
                					Toast.makeText(getBaseContext(), "Going to the Saved screen", Toast.LENGTH_SHORT).show();
                					
                					// This will start the transition to the SavedAudio screen.
                					startActivity(new Intent(this, SavedAudio.class));					
                					break;
    	}
    }
    
    /*
     * This method called to signal the completion of the TextToSpeech
     * engine initialization.
     * 
     * @param initStatus
     * 	Check to see if everything was a success or not.
     */
    public void onInit(int initStatus)
    {
    	// If the application was a sucess, it will set the language to US.
    	if(initStatus == TextToSpeech.SUCCESS)
    		
    		// Set the language to US language.
    		myTTS.setLanguage(Locale.US);
    	
    	// If the application was a error, it will display a error.
    	else if(initStatus == TextToSpeech.ERROR)
    		
    		// Display a message to tell the user, the application fail.
    		Toast.makeText(this, "Sorry! Text To Speech failed...", Toast.LENGTH_LONG).show();
    }
    
    /*
     * This method will speak the words they user type in.
     */
    private void speakWords(String speech) 
    {
    	// This will speak what the user wrote in the textfield.
    	myTTS.speak(speech, TextToSpeech.QUEUE_FLUSH, null);
    }
    
    /*
     *  Called when an activity onActivityForResult(checkTTSIntent) exits.
     *  
     *  @param requestCode
     *   This pass in the request code.
     *   
     *  @param resultCode
     *   This pass in the result code.
     *   
     *  @param data
     *   This pass in the data through intent.
     *
     */
    protected void onActivityResult(int requestCode, int resultCode, Intent data)
    {
    	if(requestCode == MY_DATA_CHECK_CODE)
    	{
    		if(resultCode == TextToSpeech.Engine.CHECK_VOICE_DATA_PASS)
    		{
    			myTTS = new TextToSpeech(this, this);
    		}
    		else
    		{
    			Intent installTTSIntent = new Intent();
    			installTTSIntent.setAction(TextToSpeech.Engine.ACTION_INSTALL_TTS_DATA);
    			startActivity(installTTSIntent);
    		}
    	}
    }
	
    /*
     *  Save the audio to a file for future use
     */
	private void saveAudioToFile()
	{
		String dataFilePath = test_to_speech_directory + File.separatorChar + "FileInformationFile.dat";
		ArrayList<FileInformation> fileInfo = null;
		final String words = txtInput.getText().toString();
  		final int maxFileNameLength = 25;
    	String saveText, previewName;
    	int spacePosition;
    	
    	// If there is text in the text input area, branch.
    	if(words.length()>0)
    	{
        	saveText = words;
        	// Check to see if the text is longer than max file length.
        	if(saveText.length() > maxFileNameLength)
        		previewName = saveText.substring(0, maxFileNameLength) + "...";
        	else
        		previewName = saveText;
        	// Check to see if the text contains a space character.
        	spacePosition = saveText.indexOf(" ");
        	if(spacePosition > 0) 
        	{
        		// Set the file name as a substring of original, up to the first space character.
        		if(Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState()))
	        		destFileName = test_to_speech_directory + File.separatorChar + saveText.substring(0, spacePosition) + ".wav"; 
        	}	
        	
        	else 
        	{
        		// Set the file name as the original text.
        		if(Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState()))
	        		destFileName = test_to_speech_directory + File.separatorChar + saveText + ".wav";
        	}
        	
        	Random rand = new Random(); 
        	
        	// Find duplicate file names. If one exists, append random number to end before .wav.
        	if(findDups(destFileName, dataFilePath))
        	{
        		if(spacePosition>0) 
        		{
            		if(Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState()))
    	        		destFileName = test_to_speech_directory + File.separatorChar + saveText.substring(0, spacePosition) + rand.nextInt(20) + ".wav"; 
            	}	
            	else 
            	{
            		if(Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState()))
    	        		destFileName = test_to_speech_directory + File.separatorChar + saveText + rand.nextInt(20) + ".wav";
            	}
        	}
        	
        	// Hash map with key/value of the unique utterance id and the preview name.
    		HashMap<String, String> myHashRender = new HashMap();
        	myHashRender.put(TextToSpeech.Engine.KEY_PARAM_UTTERANCE_ID, previewName);
        	
        	try
        	{
        		// Attempt to make the file.
        		if(myTTS.synthesizeToFile(saveText, myHashRender, destFileName) == TextToSpeech.SUCCESS)
        		{        		
        			// Make a suitable path to a file on the external storage.
        			String state = Environment.getExternalStorageState();
        			if(Environment.MEDIA_MOUNTED.equals(state))
        			{
        				// Create some data and fill up array.
        				audioFileArrayList = GetFileInfo(previewName, destFileName);
        				ObjectInputStream in = null;
        				try 
        				{
        					in = new ObjectInputStream(new FileInputStream(dataFilePath));
        				} 
        				
        				catch (StreamCorruptedException e) 
        				{
        					// TODO Auto-generated catch block
        					e.printStackTrace();
        				} 
        				
        				catch (FileNotFoundException e) 
        				{
        					// TODO Auto-generated catch block
        					e.printStackTrace();
        				} 
        				
        				catch (IOException e) 
        				{
        					// TODO Auto-generated catch block
        					e.printStackTrace();
        				}
        				
        				// if the ObjectInputStream is empty, create a new OutputStream and write ArrayList to disk
        				if(in == null)
        				{
        					saveArrayToDisk(audioFileArrayList, dataFilePath);
        					Toast.makeText(this.getBaseContext(), "New data file created. Saved successfully!", Toast.LENGTH_SHORT).show(); 
        				}
        				
        				else
        				{
        					try 
        					{
        						fileInfo = (ArrayList<FileInformation>) in.readObject();
        					} 
        					
        					catch (OptionalDataException e) 
        					{
        						// TODO Auto-generated catch block
        						e.printStackTrace();
        					} 
        					
        					catch (ClassNotFoundException e) 
        					{
        						// TODO Auto-generated catch block
        						e.printStackTrace();
        					} 
        					
        					catch (IOException e) 
        					{
        						// TODO Auto-generated catch block
        						e.printStackTrace();
        					}
        					
        					fileInfo.addAll(audioFileArrayList);
        					saveArrayToDisk(fileInfo, dataFilePath);
        					
        					// Display a message to the user that the text was saved.
        					Toast.makeText(getBaseContext(), "Text-To-Speech Saved!", Toast.LENGTH_SHORT).show();
        				}
        			}
        		}
        		else
        			// Display a message to the user that their text was not saved.
        			Toast.makeText(getBaseContext(), "Oops! Something happened. Your text was not saved.", Toast.LENGTH_SHORT).show();
        	} 
        		catch(Exception e) 
        		{
        			// Display a message that synthesizing the file failed.
        			Toast.makeText(getBaseContext(), "synthesizeToFile Failed", Toast.LENGTH_SHORT).show();
        			e.printStackTrace();
        		}
    	}
    	else
    		// Display a message that the user did not type any text to save.
    		Toast.makeText(getBaseContext(), "Sorry! You have to type text in order to save.", Toast.LENGTH_SHORT).show();
	}
	
	/*
	 *  Display/Hide the on-screen keyboard
	 */
	private void toggleKeyboard()
	{
		mIsKeyPadButtonChecked = !mIsKeyPadButtonChecked;
		
  		if(mIsKeyPadButtonChecked)
  		{
  			mKeyPadButton.postDelayed(new Runnable() 
  			{
  				public void run()
  				{
  					InputMethodManager keyboard = (InputMethodManager) getSystemService(INPUT_METHOD_SERVICE);
  					keyboard.showSoftInput(txtInput , 0);
  				}
  			}, 200);
  		}
  		else 
  		{
  			mKeyPadButton.postDelayed(new Runnable()
  			{
  				public void run()
  				{
  					InputMethodManager keyboard = (InputMethodManager) getSystemService(INPUT_METHOD_SERVICE);
  					keyboard.hideSoftInputFromWindow(txtInput.getWindowToken() , 0);
  				}
  			}, 200);
  		}
	}
	
	/*
	 *  If Speak button is clicked, executes speakWords method and shows ProgressBar
	 */
	private void speakUtterance()
	{
		final String words = txtInput.getText().toString();
    	try {
    		if(words.length()>0)
        	{
        		AsyncTask<Void,Void,Void> lTask = new AsyncTask<Void,Void,Void>()
        		{

            		ProgressDialog lDialog;
            		protected void onPreExecute()
            		{
            		              lDialog = ProgressDialog.show(mContext, "","Your msg",true);
            		}

            		protected Void doInBackground(Void... params)
            		{
            		              speakWords(words);
            		              
            		              return null;
            		}

            		protected void onPostExecute(Void result)
            		{
            			Thread myThread = new Thread()
            			{
            				public void run()
            				{
            					try 
            					{
            						sleep(1000);
            					} 
            					
            					catch(InterruptedException e) 
            					{
            						e.printStackTrace();
            					} 
            					
            					finally 
            					{
            						lDialog.dismiss();
            					}
            				}
            			};
            			myThread.start();
            		}
            	};
            	lTask.execute();
        	}
    		else 
    			Toast.makeText(getApplicationContext(), "You have to type words first!", Toast.LENGTH_SHORT).show();        		
    	} 
    	
    	catch(Exception e) 
    	{
    		Toast.makeText(getParent(), "Something else went wrong!", Toast.LENGTH_SHORT).show();
    		e.printStackTrace();
    	}
	}
	
	/*
	 *  Return an array list that contains file information
	 */
	private ArrayList<FileInformation> GetFileInfo(String strPrevName, String strDestName)
	{
		ArrayList<FileInformation> file_info = new ArrayList<FileInformation>();
		FileInformation fi = new FileInformation(strPrevName, strDestName);
		file_info.add(fi);
		
		return file_info;
	}
	
	/*
	 * This method will save the data to the disk.
	 */
	private void saveArrayToDisk(ArrayList<FileInformation> myArray, String myPath)
	{
		try 
		{
			// Write array to disk
			File file = new File(myPath);
			
			// If the file already exist, delete it.
			if(file.exists())
				
				// Delete the file.
				file.delete();
			
			ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream(myPath));
			out.writeObject(myArray);
			out.flush();
			out.close();
		}
		
		catch(FileNotFoundException e)
		{
			Toast.makeText(getBaseContext(), "File Not Found.", Toast.LENGTH_SHORT).show();
			e.printStackTrace();
		}
		
		catch(IOException e)
		{
			Toast.makeText(getBaseContext(), "IO Exception.", Toast.LENGTH_SHORT).show();
			e.printStackTrace();
		}
		
		catch(Exception e)
		{
			Toast.makeText(getBaseContext(), "Exception!", Toast.LENGTH_SHORT).show();
			e.printStackTrace();
		}
	}
	
	/*
	 *  Find duplicate file names.
	 */
	private boolean findDups(String myAudioFilePath, String myDataFilePath)
	{
		ArrayList<FileInformation> myList = null;
		ObjectInputStream in = null;
		
		// Try to create a file input stream to read in the data file path.
		try 
		{
			in = new ObjectInputStream(new FileInputStream(myDataFilePath));
			
		} 
		
		catch (StreamCorruptedException e) 
		{
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		
		catch (FileNotFoundException e) 
		{
			// TODO Auto-generated catch block
			e.printStackTrace();
		} 
		
		catch (IOException e) 
		{
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		
		// If creation of file input stream was successful, branch
		if(in != null)
		{
			// Try to read the object from the file input stream
			try 
			{
				myList = (ArrayList<FileInformation>) in.readObject();
			} 
			
			catch (OptionalDataException e) 
			{
				// TODO Auto-generated catch block
				e.printStackTrace();
			} 
			
			catch (ClassNotFoundException e) 
			{
				// TODO Auto-generated catch block
				e.printStackTrace();
			} 
			
			catch (IOException e) 
			{
				// TODO Auto-generated catch block
				e.printStackTrace();	
			}
			
			// Loop through the arraylist to compare the path names.
			for(int i = 0;i < myList.size(); i++)
			{
				if(myAudioFilePath.equals(myList.get(i).getDestinationFileName()))
					return true;
			}
		}
		return false;
	}

	
	/*
	 * This method is a auto generated method.
	 */
	@Override
	protected void onDestroy() 
	{
		// TODO Auto-generated method stub
		super.onDestroy();
	}

	
	/*
	 * This method is a auto generated method.
	 */
	@Override
	protected void onPause() 
	{
		// TODO Auto-generated method stub
		super.onPause();
	}

	/* 
	 * This method will add the directory path to the program.
	 */
    private void createDirectory(String directory_path) 
    {
    	// Initialize a new file variable and set it to that path.
    	File f  = new File(directory_path + File.separatorChar );
    	
    	// Print out the path of the file.
    	System.out.println(directory_path);
    	
    	// If the file does not exist, it will create one or it will
    	// display a message that the folder cannot be created.
    	if(!f.exists())
    	{
    		// This boolean will store if the folder was created.
    		boolean success = f.mkdir();
			
    		// If it not successful it will display a message.
    		if(!success) 
			{
    			// Display a message that the folder could not be created.
				Toast.makeText(getBaseContext(), directory_path + " could not be created", 15).show();
            }
    	}
    }	
}